<?

/* NAVIGATORE DI XML 

API

********************************************************************************
function GET
--------------------------------------------------------------------------------
params: 	[0] Path			(not null)
				Indica il path da restituire
			[1] xmlPosition		
				Indica l'eventuale nodo di partenza
			[2] utilParameters 
				Eventuali parametri. Es:
					'createIfNull' => Crea nodi e attributi se non esistono, valorizzandoli se richiesto o lasciandoli = ""
					'getArrayValue' => Restituisce l'array di valori
********************************************************************************

*/


class FFxml {
	var $arrOutput = array();
	var $resParser;
	var $strXmlData;
	var $debugStatus=false;	
	
	function FFxml($tfile = "", $debugTypes = array()) {
		if(trim($tfile) != "") {
			$this->xmlFile = $tfile;
			$this->loadFile($tfile);
		}
		// Gestisce la tipologia di debug
		if(count($debugTypes) > 0) $this->debugTypes = $debugTypes;
		else $this->debugTypes = false;
		//$this->tab = '\t';
	}
	
	function loadFile($tfile) {
		$this->thefile = $tfile;
		
		$th = file($tfile);
		$tdata = implode("\n", $th);
		
		return $this->parse($tdata);
	}
	
	function parse($strInputXML) {
		$this->resParser = xml_parser_create ();
		xml_set_object($this->resParser, $this);
		xml_set_element_handler($this->resParser, "tagOpen", "tagClosed");
		
		xml_set_character_data_handler($this->resParser, "tagData");
		
		$this->strXmlData = xml_parse($this->resParser, $strInputXML );
		
		if($this->strXmlData === false) {
			die(sprintf("XML error: %s at line %d",
			xml_error_string(xml_get_error_code($this->resParser)),
			xml_get_current_line_number($this->resParser)));
		}
		
		xml_parser_free($this->resParser);
		
		return $this->arrOutput;
	}
	
	//called on each xml tree
	function tagOpen($parser, $name, $attrs) {
		$tag=array("nodename"=>$name,"attributes"=>$attrs);
		array_push($this->arrOutput, $tag);
	}
	
	//called on data for xml
	function tagData($parser, $tagData) {
		if(trim($tagData) || is_numeric($tagData)) {
			if(isset($this->arrOutput[count($this->arrOutput)-1]['nodevalue']))
				$this->arrOutput[count($this->arrOutput)-1]['nodevalue'] .= $this->parseXMLValue($tagData);
			else
				$this->arrOutput[count($this->arrOutput)-1]['nodevalue'] = $this->parseXMLValue($tagData);
		}
	}
		
	//called when finished parsing
	function tagClosed($parser, $name) {
		$this->arrOutput[count($this->arrOutput)-2]['childrens'][] = $this->arrOutput[count($this->arrOutput)-1];
		
		if(count ($this->arrOutput[count($this->arrOutput)-2]['childrens'] ) == 1) {
			$this->arrOutput[count($this->arrOutput)-2]['firstchild'] =& $this->arrOutput[count($this->arrOutput)-2]['childrens'][0];
		}
		array_pop($this->arrOutput);
	}
	
	function parseXMLValue($tvalue) {
		$tvalue = htmlentities($tvalue);
		return $tvalue;
	}
	
	function debug($text, $type, $echo) {
		if(in_array($type, $this->debugTypes) || in_array('ALL', $this->debugTypes)) {
			switch ($type) { 
				case 'get':
					$br = "<hr>";
					break;
				default:
					$br = "<br>";
					break;
			}
			echo "$br".strToUpper($type)."> ";
			if($echo === false || $echo === null) echo $text;
			else if ($echo == 'var_dump') var_dump($text);
		}
	}
	
	function getNodeAttribute($attributeName, $node, $utilParams) {
		switch($attributeName) {
			case 'nodeValue':
				if(array_key_exists(strToLower($attributeName), $node) === false && !is_numeric(array_key_exists(strToLower($attributeName), $node))) $node['nodevalue'] = "";
				$this->appendToKeyMapper($i, 'nodevalue');
				return $node[strToLower($attributeName)];
				break;
			case 'childNodes':
				if(array_key_exists('childrens', $node)) return $node['childrens'];
				else return false;
				break;
			case 'node':
				return $node;
				break;
			default:
				if(array_key_exists(strToUpper($attributeName), $node['attributes'])) return $node['attributes'][strToUpper($attributeName)];
				else {
					if(in_array('createIfNull', $utilParams)) {
						echo "<br />Creo l'attributo che manca!!!!!!";
					}
				};
				break;
		}
	}
	
	function checkAttributeValue($attrName, $attrValue, $node) {
		if($node['attributes'][strToUpper($attrName)] == $attrValue) return true;
		else return false;
	}
	
	function getParentNodeKey($keyString) {
		// Trova la keyList del parentNode partendo da una keyList
		$temp = str_replace("[", "", $keyString);
		$this->debug(">>getParentNodeKey: " . $keyString, 'getParentNodeKey');
		eval('$tempArray = array('.str_replace("]", ",", $temp).');');
		$tempArray_result = $tempArray;
		for($i=0; $i<count($tempArray); $i++) {
			$this->debug("Guardo: " . $tempArray[count($tempArray)-$i-1], 'getParentNodeKey');
			$this->debug("Restituirei: " . $this->transformKeyList($tempArray_result), 'getParentNodeKey');
			if(is_numeric($tempArray[count($tempArray)-$i-1])) {
				$this->debug("<strong>Yesss!</strong> Trovato il parent ", 'getParentNodeKey');
				return $this->transformKeyList($tempArray_result);
			} else array_pop($tempArray_result);
		}
	}
	
	function getKeyLister($val) {
		if(is_finite($val)) return "[$val]";
		else return "['$val']";
	}
	
	function transformKeyList($keys) {
		$keys = array_map(array('FFxml','getKeyLister'), $keys);
		return implode("", $keys);
	}
	
	function getKeyList($keys) {
		$this->debug("Init pos key: " . $this->initPosKeys, 'getKeyList');
		$this->debug("keys passate:", 'getKeyList');
		$this->debug($keys, 'getKeyList', 'var_dump');
		if(($keys === null || $keys === false || !isset($key)) && !is_array($keys)) $keys = $this->keyMapper;
		$keys = array_map(array('FFxml','getKeyLister'), $keys);
		return $this->initPosKeys . implode("", $keys);
	}
	
	function appendToKeyMapper($key, $type) {
		$this->debug("Aggiungo: $key, $type", "appendToKeyMapper");
		if($type === null) {
			if($key == 'childNodes') array_push($this->keyMapper, 'childrens');
			else if($key == 'nodeValue') array_push($this->keyMapper, 'nodevalue');
			else if($key == 'node') {}
		} else {
			if(count($this->keyMapper) > 0) {
				array_push($this->keyMapper, $type);
			}
			if($type == 'attributes') $key = strToUpper($key);
			if($type != 'nodevalue') array_push($this->keyMapper, $key);
		}
		$this->debug("Risultato ora (tramite getKeyList): " . $this->getKeyList(), "appendToKeyMapper");
		eval('$can = $this->arrOutput[0][\'childrens\'][3][\'childrens\'][0][\'childrens\'][0][\'childrens\'][0][\'attributes\'][\'NAME\'];');
		$this->debug("L'eval delle chiavi qui sopra, da: " . $can, "appendToKeyMapper");
	}
	
	function isReservedAttribute($attribute) {
		if($attribute == 'childNodes' || $attribute == 'nodeValue' || $attribute == 'node') return true;
		else return false;
	}
	
	function getNode($nodeName, $utilParams) {
		$return = false;
		$this->debug('** Chiamato getNODE ('.$nodeName.' -> keyList:'.$this->getKeyList().') **', 'getNode');
		if(strpos($nodeName, '.') === false) {
			// Cerco solo il nodo
			if($this->arrWatcher['nodename'] === null || $this->arrWatcher['nodename'] === "" || $this->arrWatcher['nodename'] === false) {
				for($i=0; $i<count($this->arrWatcher); $i++) {
					if($this->arrWatcher[$i]['nodename'] == $nodeName && !$this->nodeFound) {
						if(array_key_exists('childrens', $this->arrWatcher[$i])) {
							$this->debug("Ho figli, li restituisco", "getNode");
							$return = &$this->arrWatcher[$i]['childrens'];
							$this->appendToKeyMapper($i, 'childrens');
						} else {
							$this->debug("Non ho figli", "getNode");
							$this->appendToKeyMapper($i, 'childrens');
							$return = &$this->arrWatcher[$i];
						}
					}
				}
			} else {
				for($i=0; $i<count($this->arrWatcher['childrens']); $i++) {
					if($this->arrWatcher['childrens'][$i]['nodename'] == $nodeName && !$this->nodeFound) {
						$this->appendToKeyMapper($i, 'childrens');
						if(array_key_exists('childrens', $this->arrWatcher['childrens'][$i])) $return = &$this->arrWatcher['childrens'][$i]['childrens'];
						else $return = &$this->arrWatcher['childrens'][$i];
					}
				}
			}
			if($return === false) {
				
			}
			//if($return === false && in_array('createIfNull', $utilParams)) {$this->appendChildnode($this->arrWatcher, $this->createNode(array($nodeName)));}
		} else {
			// Ho attributi da cercare
			$this->debug("Ho attributi da cercare (this->arrWatcher): ", "getNode");
			$this->debug($this->arrWatcher, "getNode", "var_dump");
			list($nodeName1, $attributeName, $attributeGet) = explode(".", $nodeName);
			if(count($this->arrWatcher) == 0 && in_array('createIfNull', $utilParams)) {
				$this->appendChildnode($this->arrWatcher, $this->createNode(array($nodeName1)));
				$this->arrWatcher = $this->arrWatcher['childrens'];
				$this->debug('count($this->arrWatcher) === 0 && in_array("createIfNull", $utilParams)', 'getNode');
			}
			
//			if(array_key_exists("childrens", $this->arrWatcher))
			for($i=0; $i<count($this->arrWatcher); $i++) {
				if($this->arrWatcher[$i]['nodename']) $watching = &$this->arrWatcher[$i];
				else $watching = &$this->arrWatcher;
				$this->debug("guardo se ".$watching['nodename']." e' uguale a ".$nodeName1. " -- " . $this->arrWatcher['nodename'], "getNode");
				if($watching['nodename'] === $nodeName1) {
					$this->debug("Trovato il nodo : $nodeName1", 'getNode');
					if(strpos($attributeName, '=') !== false) {
						$this->debug("Ho un'equazione da cercare ($attributeName)", 'getNode');
						// Ho un'equazione da cercare
						list($attributeSearchName, $attributeSearchValue) = explode("=", $attributeName);
						if($this->checkAttributeValue($attributeSearchName, $attributeSearchValue, $watching)) {
							$this->debug("Ho trovato l'attributo $attributeSearchName che vale $attributeSearchValue", 'getNode');
							$this->appendToKeyMapper($i, 'childrens');
							if($attributeGet != null) {
								$this->debug("Mi serve l'attributo $attributeGet", 'getNode');
								if($this->isReservedAttribute($attributeGet)) $this->appendToKeyMapper($attributeGet);
								else $this->appendToKeyMapper($attributeGet, 'attributes');
								$return = $this->getNodeAttribute($attributeGet, $watching);
							} else {
								if(array_key_exists("childrens", $watching)) $return = $watching['childrens'];
								else $return = $watching;
							}
						} else {
//							echo "<br>non trovata l'equazione: $attributeName";
						}
					} else {
						// Cerco semplicemente un attributo di un nodo
						$this->debug("Non cerco equazioni ma solo l'attributo: $attributeName", 'getNode');
						$this->appendToKeyMapper($i, 'childrens');
						$return = $this->getNodeAttribute($attributeName, $watching, $utilParams);
						if ($return !== false || is_numeric($return)) {
							$this->debug("Trovato!! (".$return.")", 'getNode');
							$this->nodeFound = true;
						} else {
							$this->debug("non trovato!!", 'getNode');
							return false;
						}
					}
				}
			}
		}
		if($return !== false || is_numeric($return)) {
			$this->debug('Trovato il nodo [FINE GETNODE]', 'getNode');
			$this->nodeFound = true;
			return $return;
		} else {
			$this->debug('Non ho trovato il nodo', 'getNode');
			if(in_array('createIfNull', $utilParams)) {
				$this->debug('Esiste: createIfNull. Creo il nodo che cercavo<br />', 'getNode');
				list($nodeName, $attributeName, $attributeGet) = explode(".", $nodeName);
				$this->debug("GetKeyList: " . $this->getKeyList(), 'getNode');
				$this->debug("Padre: " . $this->getKeyList($parentNodeKey), 'getNode');
				eval('$parentNode = &$this->arrOutput'.$this->getParentNodeKey($this->getKeyList()).';');
				$return = &$this->appendChildnode($parentNode, $this->createNode(array($nodeName, $attributeName, $attributeGet)), true);
				if($attributeGet) $this->appendToKeyMapper($attributeGet, 'attributes');
				else if($attributeName) $this->appendToKeyMapper($attributeName, 'attributes');
				if($return !== false) {
					$this->debug('Nodo creato correttamente (o comunque ho fatto un appendchild corretto)', 'getNode');
					$this->nodeFound = true;
					return $return;
				}
			}
		}
		
	}
	
	function remove($path) {
		$res = $this->get($path, false, array('createIfNull'));
		eval('unset($this->arrOutput'.$this->getKeyList().');');
	}
	
	function removeChildnode($parentNode, $node) {
		$res = $this->get($parentNode, false, array('createIfNull'));
		eval('$parentNode = &$this->arrOutput'.$this->getKeyList().';');
		if(array_key_exists("childrens", $parentNode) && count($parentNode['childrens'])) {
			echo "entro<br/>";
			for($i=0; $i<count($parentNode['childrens']); $i++) {
				if((is_numeric($node) && $i == $node) || ($parentNode['childrens'][$i]['nodename'] == $node)) {
					unset($parentNode['childrens'][$i]);
					$this->debug("Nodo trovato e rimosso correttamente", "removeChildnode");
					return true;
				}
			}
		}
		return false;
	}
	
	function setValue($path, $value, $xmlPos) {
		if(!$xmlPos || $xmlPos === false || $xmlPos === null) $xmlPos = false;
		$res = $this->get($path, $xmlPos, array('createIfNull'));
		$this->debug("RES del get: ", 'setValue');
		$this->debug($res, 'setValue', 'var_dump');
		$this->debug("il get ha fornito queste chiavi: ".$this->getKeyList(), 'setValue');
		eval('$this->arrOutput'.$this->getKeyList().' = "'.$value.'";');
	}
	
	function setAttribute($path, $attributeName, $value, $xmlPos) {
		$this->debug("<strong>: ($path, $value)</strong>", 'setValue');
		if(!$xmlPos || $xmlPos === false || $xmlPos === null) $xmlPos = false;
		$res = $this->get($path, $xmlPos, array('createIfNull'));
		$this->debug("RES del get: ", 'setValue');
		$this->debug($res, 'setValue', 'var_dump');
		$this->debug("il get ha fornito queste chiavi: ".$this->getKeyList(), 'setValue');
		eval('$this->arrOutput'.$this->getKeyList().'[attributes]['.strToUpper($attributeName).'] = "'.$value.'";');
	}
	
	function setValueByKey($path, $key, $value) {
		$this->get($path, false, array('createIfNull'));
		eval('$this->arrOutput'.$this->getKeyList().$key.' = "'.$value.'";');
	}
	
	function get($path, $initPos, $utilParams) {										
		
		$this->debug("<strong>($path)</strong>", 'get');
		$this->searchNodes = explode(">", $path);
		$this->keyMapper = array();
		$this->initPosKeys = "";
		
		if(isset($initPos) && $initPos != null) {
			if(is_string($initPos)) {
				eval('$this->arrWatcher = $this->arrOutput'.$initPos.';');
				$this->initPosKeys = $initPos;
			} else {
				$this->arrWatcher = array($initPos);
			}
		} else $this->arrWatcher = $this->arrOutput;
		
		foreach($this->searchNodes as $getNode) {
			$this->nodeFound = false;
			$this->arrWatcher = $this->getNode($getNode, $utilParams);
		}
		$this->debug("<strong>fine get</strong>", 'get');
		if($this->nodeFound !== false || is_numeric($this->nodeFound)) {
			return $this->arrWatcher;
		} else {
			//echo "<br>non ho trovato nulla ($path)<br>";
			return false;
		}
		
	}
	
	function appendChildnode(&$parentNode, $childNode, $updateKeyMapper) {
		$this->debug("Chiamato. ParentNode:", 'appendChildnode');
		$this->debug($parentNode, 'appendChildnode', 'var_dump');
		if(array_key_exists('childrens', $parentNode)) {
			$this->debug("Il parentNode ha l'attributo childrens", 'appendChildnode');
			array_push($parentNode['childrens'], $childNode);
		} else {
			$this->debug("Il parentNode non ha l'attributo childrens. Lo creo", 'appendChildnode');
			$parentNode['childrens'] = array();
			array_push($parentNode['childrens'], $childNode);
		}
		if($updateKeyMapper) $this->appendToKeyMapper(count($parentNode['childrens'])-1, 'childrens');
		$this->debug("Fine append, ritorno:", 'appendChildnode');
		$this->debug($parentNode['childrens'][count($parentNode['childrens'])-1], 'appendChildnode', 'var_dump');
		return $parentNode['childrens'][count($parentNode['childrens'])-1];	
	}
	
	function appendNewNode($path, $node) {
		$this->get($path);
		eval('$parentNode = &$this->arrOutput'.$this->getKeyList().';');
		if(!array_key_exists('childrens', $parentNode)) $parentNode['childrens'] = array();
		array_push($parentNode['childrens'], $node);
	}
	
	function createNode($nodeParams) {
		
		$return = array();
		$return['nodename'] = $nodeParams[0];
		$return['childrens'] = array();
		$return['attributes'] = array();
		
		for($i=1; $i<count($nodeParams); $i++) {
			if($nodeParams[$i]) {
				list($attributeName, $attributeValue) = explode('=', $nodeParams[$i]);
				if($attributeName != null && $attributeName != 'childrens' && $attributeName != 'nodevalue' && $attributeName != 'node') {
					$this->debug("Trovato attrName: " . $attributeName . " => " . $attributeValue, 'createNode');
					$return['attributes'][strToUpper($attributeName)] = (($attributeValue != "") ? $attributeValue : "");
				}
			}
		}
		$this->debug($return, 'createNode', 'var_dump');
		return $return;
		
	}
	
	function buildXML($arr, $level) {
		
		if(!isset($arr)) {
			$this->XMLbuild = "";
			$this->arrBuilder = $this->arrOutput;
		} else $this->arrBuilder = $arr;
		if(!isset($level)) {
			$levelTabs = '';
			$level = 0;
		} else {
			for($i=0; $i<$level; $i++) {
				$levelTabs .= "\t";
			}
		}
		$watching = $this->arrBuilder;

		foreach($watching as $k => $v) {
			$nodename = $watching[$k]['nodename'];
			$this->XMLbuild .= $levelTabs."<".strToLower($nodename)."";					// Apro il nodo
			if(count($watching[$k]['attributes']) > 0) {
				foreach($watching[$k]['attributes'] as $n => $vv) {		// Ciclo gli attributi
					$this->XMLbuild .= ' '.strToLower($n).'="'.$vv.'"';
				}
				$this->XMLbuild .= " >";
			} else $this->XMLbuild .= ">";
			//$this->XMLbuild .= "\t";
			if(array_key_exists('nodevalue', $watching[$k])) {				// C'� un nodevalue
				$this->XMLbuild .= $watching[$k]['nodevalue'];
			}
			if(array_key_exists('childrens', $watching[$k]) && count($watching[$k]['childrens']) > 0) {					// Ci sono figli
				$this->XMLbuild .= "\n";
				$this->buildXML($watching[$k]['childrens'], $level+1);
			}
			$this->XMLbuild .= ((array_key_exists('nodevalue',$watching[$k]) || (!array_key_exists('nodevalue',$watching[$k]) && $watching[$k]['childrens'] === 0))?"":$levelTabs)."</".strToLower($nodename).">\n";
		}
	}
	
	function saveBuiltXML($doBackup) {
		
		$this->buildXML();
		
		if($doBackup) {
			$backup = explode(".xml", $this->thefile);
			$backupFile = $backupFile[0]."_BACKUP_".date("Y-m-d")."_".time().".xml";
			$date = date("Y-m-d");
			
			copy($this->thefile, $backupFile[0]."_BACKUP_".date("Y-m-d")."_".time().".xml");
		}
		
		$fp = fopen($this->thefile, 'w+');
		fwrite($fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" . $this->XMLbuild);
		fclose($fp);
		
	}
	
}//end of FFxml class


class FF extends FFxml {

	function FF() 
	{
		
	}
	
	function isVirtualCross($f, $t)
	{
		$this->getNode();
	}

}

?>